#include "dvec2.h"
#include "dvec3.h"
#include "vec4.h"
#include "dvec4.h"
#include "dmat4.h"

namespace libvec
{
  dvec4::dvec4()
  {
    this->x = 0;
    this->y = 0;
    this->z = 0;
    this->w = 0;
  }

  dvec4::dvec4(vec4 v)
  {
    this->x = v.x;
    this->y = v.y;
    this->z = v.z;
    this->w = v.w;
  }

  dvec4::dvec4(double value)
  {
    this->x = value;
    this->y = value;
    this->z = value;
    this->w = value;
  }

  dvec4::dvec4(dvec3 v, double w)
  {
    this->x = v.x;
    this->y = v.y;
    this->z = v.z;
    this->w = w;
  }

  dvec4::dvec4(dvec2 v, double z, double w)
  {
    this->x = v.x;
    this->y = v.y;
    this->z = z;
    this->w = w;
  }

  dvec4::dvec4(double x, double y, double z, double w)
  {
    this->x = x;
    this->y = y;
    this->z = z;
    this->w = w;
  }

  double &dvec4::operator[](int index)
  {
    static double empty = 0;

    switch (index)
    {
      case 0:
        return this->x;

      case 1:
        return this->y;

      case 2:
        return this->z;

      case 3:
        return this->w;
    }

    return empty;
  }

  dvec4 dvec4::operator=(dvec4 v)
  {
    this->x = v.x;
    this->y = v.y;
    this->z = v.z;
    this->w = v.w;

    return *this;
  }

  bool dvec4::operator==(dvec4 v)
  {
    return (this->x == v.x) && (this->y == v.y) && (this->z == v.z) &&
      (this->w == v.w);
  }

  bool dvec4::operator!=(dvec4 v)
  {
    return (this->x != v.x) || (this->y != v.y) || (this->z != v.z) ||
      (this->w != v.w);
  }

  dvec4 &dvec4::operator+()
  {
    return *this;
  }

  dvec4 &dvec4::operator-()
  {
    static dvec4 out;

    out.x = -this->x;
    out.y = -this->y;
    out.z = -this->z;
    out.w = -this->w;

    return out;
  }

  dvec4 dvec4::operator+(dvec4 v)
  {
    static dvec4 out;

    out.x = this->x + v.x;
    out.y = this->y + v.y;
    out.z = this->z + v.z;
    out.w = this->w + v.w;

    return out;
  }

  dvec4 dvec4::operator+=(dvec4 v)
  {
    this->x += v.x;
    this->y += v.y;
    this->z += v.z;
    this->w += v.w;

    return *this;
  }

  dvec4 dvec4::operator-(dvec4 v)
  {
    static dvec4 out;

    out.x = this->x - v.x;
    out.y = this->y - v.y;
    out.z = this->z - v.z;
    out.w = this->w - v.w;

    return out;
  }

  dvec4 dvec4::operator-=(dvec4 v)
  {
    this->x = this->x - v.x;
    this->y = this->y - v.y;
    this->z = this->z - v.z;
    this->w = this->w - v.w;

    return *this;
  }

  dvec4 dvec4::operator*(dmat4 m)
  {
    static dvec4 out;

    out.x = this->x*m.v1.x + this->y*m.v1.y + this->z*m.v1.z + this->w*m.v1.w;
    out.y = this->x*m.v2.x + this->y*m.v2.y + this->z*m.v2.z + this->w*m.v2.w;
    out.z = this->x*m.v3.x + this->y*m.v3.y + this->z*m.v3.z + this->w*m.v3.w;
    out.w = this->x*m.v4.x + this->y*m.v4.y + this->z*m.v4.z + this->w*m.v4.w;

    return out;
  }

  dvec4 dvec4::operator*=(dmat4 m)
  {
    static dvec4 out;

    this->x = this->x*m.v1.x + this->y*m.v1.y + this->z*m.v1.z + this->w*m.v1.w;
    this->y = this->x*m.v2.x + this->y*m.v2.y + this->z*m.v2.z + this->w*m.v2.w;
    this->z = this->x*m.v3.x + this->y*m.v3.y + this->z*m.v3.z + this->w*m.v3.w;
    this->w = this->x*m.v4.x + this->y*m.v4.y + this->z*m.v4.z + this->w*m.v4.w;

    return out;
  }

  dvec4 dvec4::operator*(double scale)
  {
    static dvec4 out;

    out.x = this->x * scale;
    out.y = this->y * scale;
    out.z = this->z * scale;
    out.w = this->w * scale;

    return out;
  }

  dvec4 dvec4::operator*=(double scale)
  {
    this->x *= scale;
    this->y *= scale;
    this->z *= scale;
    this->w *= scale;

    return *this;
  }

  dvec4 dvec4::operator/(double scale)
  {
    static dvec4 out;

    out.x = this->x / scale;
    out.y = this->y / scale;
    out.z = this->z / scale;
    out.w = this->z / scale;

    return out;
  }

  dvec4 dvec4::operator/=(double scale)
  {
    this->x /= scale;
    this->y /= scale;
    this->z /= scale;
    this->w /= scale;

    return *this;
  }
}

